package gbench.appdemo.myworld.market.license;

import static gbench.common.tree.LittleTree.IRecord.REC;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.appdemo.myworld.market.SimpleMarket;
import gbench.appdemo.myworld.market.SimpleMarket.DRCR;
import gbench.appdemo.myworld.market.commodity.Commodity;
import gbench.appdemo.myworld.market.commodity.Valuable;
import gbench.common.tree.LittleTree.IRecord;
import lombok.Data;

/**
 * 商品交易许可组
 * @author gbench
 *
 */
@Data
public class CommodityLicenseGroup{
    
    /**
     * 生成一个 交易许可组
     * @param participantId
     * @param licenses
     */
    @SafeVarargs
    public CommodityLicenseGroup(String participantId,CommodityLicense<Commodity,Commodity> ... licenses ){
        this.participantId = participantId;
        this.licenses = Arrays.asList(licenses);
    }
    
    /**
     * 
     * @param name
     * @param licenses
     */
    public CommodityLicenseGroup(String name,Map<String,CommodityLicense<Commodity,Commodity>> licenses ){
        this.participantId = name;
        this.licenses = licenses.values().stream().collect(Collectors.toList());
    }
    
    /**
     * 
     * @return
     */
    public Stream<SimpleMarket<Commodity>.TradeTransaction<Commodity, Commodity>>  getTransaction(){
       
        return licenses.stream().map(e->e.getMarket()).distinct().flatMap(e->e.getTransactions().stream());
    }
    
    /**
     * 获取 复式记账方法
     * @param supplier
     * @return IRecord 的流
     */
    public Stream<IRecord> getDoubleEntries(Supplier<Stream<SimpleMarket<Commodity>.TradeTransaction<Commodity, Commodity>>> supplier){
        final var ai = new AtomicInteger(1);// 起始序号
        
        return supplier.get().flatMap(e->{
            final var price = e.getPrice();
            final var a = price.getA();
            final var b = price.getB();
            final var na = price.getAAmount();
            final var nb = price.getBAmount();
            final var drcr_mode = e.getDrcr(); // 交易分录的 记账模式
            final List<IRecord> recs = new LinkedList<>();
            final var proto=REC("transCode",ai.getAndIncrement(),
                "time",e.getTime(),
                "partA",e.getPartA(),
                "partB",e.getPartB(),
                "price",price
            );// 交易记录头
            
            // 结果默认为为 贷出模式 即 drcr_mode 为 DRCR.CR 
            if(e.getPartA().getCode().equals(this.getParticipantId())) {// 购入商品
                IRecord dr = proto.derive("drcr",0,"name",b,"amount",nb);
                IRecord cr = proto.derive("drcr",1,"name",a,"amount",na);
                recs.add(dr);recs.add(cr);
            }else if(e.getPartB().getCode().equals(this.getParticipantId())) { // 出售商品
                IRecord dr = proto.derive("drcr",0,"name",a,"amount",na);
                IRecord cr = proto.derive("drcr",1,"name",b,"amount",nb);
                recs.add(dr);recs.add(cr);
            }else {
                // System.err.println("discard:"+e);
            }// if
            
            if(drcr_mode==DRCR.DR) {// 如果是贷出模式 进行反向计入。
                recs.forEach(r->{
                    r.compute("drcr", o->o.equals(0)?1:0);
                }); // forEach
            }// if drcr_mode
            
            return recs.stream();
        });
    }
    
    /**
     * 复式结账分录
     * @return
     */
    public Stream<IRecord> getDoubleEntries(){
        return this.getDoubleEntries(()->this.getTransaction());
    }
    
    /**
     * 试算平衡
     * @return [{name,subject,amount}]
     */
    public Stream<IRecord> trialBalance(){
        return this.trialBalance(null);
    }
    
    /**
     * 试算平衡
     * @param predicate 结果筛选器
     * @return [{name,subject,amount}]
     */
    public Stream<IRecord> trialBalance(Predicate<IRecord> predicate){
        if(predicate==null)predicate=rec->true;
        List<IRecord> recs = new LinkedList<>();
        this.getDoubleEntries()
        .collect(Collectors.groupingBy(e->e.get("name")))
        .forEach((subject,vv)->{// 按照科目进行分类
            final var rec = REC();
            final var name = ((Valuable)subject).getName(); // 获取科目名称
            final var amount = vv.stream().collect(Collectors.summarizingDouble(v->(v.get("drcr").equals(0)?1:-1)*v.i4("amount"))).getSum();
            rec.add(REC("name",name,"subject",subject,"amount",amount));
            recs.add(rec);
        });
        return recs.stream().filter(predicate);
    }
    
    /**
     * 
     * @param name 组名
     * @param licenses 角色名称
     * @return 交易祖娥色组
     */
    @SafeVarargs
    public static CommodityLicenseGroup of(String name,CommodityLicense<Commodity,Commodity> ... licenses) {
        return new CommodityLicenseGroup(name,licenses);
    }
    
    /**
     * 
     * @param name 组名
     * @param licenses 角色名称
     * @return 交易祖娥色组
     */
    public static CommodityLicenseGroup of(String name,Map<String,CommodityLicense<Commodity,Commodity>> licenses) {
        return new CommodityLicenseGroup(name,licenses);
    }
    
    private List<CommodityLicense<Commodity,Commodity>> licenses;// 许可集合
    private String participantId;// 市场参与者Id

}
